/*
 * @Description:
 * @Author: your name
 * @version:
 * @Date: 2024-05-08 14:05:51
 * @LastEditors: your name
 * @LastEditTime: 2024-05-08 14:51:55
 */
import * as Cesium from 'cesium';
import { TencentImageryProvider } from './mapPlugin';
import { cartesian3ToDegreesHeight } from './core';
import { CoordTransform } from '@/cesiumTools/mapPlugin.js';

// 初始化地图场景
export const initViewer = container => {
  // 初始化cesium地图对象viewer
  const viewer = new Cesium.Viewer(container, {
    timeline: true, // 是否显示时间线控件
    animation: false, // 是否显示动画控件
    baseLayerPicker: false, // 避免token
    infoBox: false, // 是否显示点击要素之后显示的信息
    selectionIndicator: false, //选中元素显示,默认true
    skyAtmosphere: false, //关闭地球光环
    homeButton: false,
    fullscreenButton: true,
    geocoder: false,
    sceneModePicker: false,
    shouldAnimate: true,
    navigationHelpButton: false,
    // 指定上下文
    contextOptions: {
      // 支持使用老版本webgl进行着色器编写
      requestWebgl1: true,
      // 开启纹理采样异性过滤，不开的话，视角越倾斜，越模糊
      allowTextureFilterAnisotropic: true,
      webgl: {
        alpha: false,
        depth: true,
        stencil: false,
        antialias: true,
        powerPreference: 'high-performance',
        premultipliedAlpha: true,
        preserveDrawingBuffer: false,
        failIfMajorPerformanceCaveat: false,
      },
    },
  });

  const options = {
    style: 4, //style: img、1：经典
    crs: 'WGS84',
  };
  // 添加腾讯底图
  viewer.imageryLayers.add(new Cesium.ImageryLayer(new TencentImageryProvider(options)));
  return viewer;
};

// 设置后处理效果
export const setScene = viewer => {
  //隐藏太阳
  viewer.scene.sun.show = true;
  //隐藏月亮
  viewer.scene.moon.show = false;
  viewer.scene.globe.enableLighting = true; // 开启全球光照

  // 泛光设置
  const bloom = viewer.scene.postProcessStages.bloom;
  bloom.enabled = false;

  // 亮度设置
  // 亮度设置
  var stages = viewer.scene.postProcessStages;
  viewer.postProcessStages.fxaa.enabled = true;
  viewer.scene.brightness = viewer.scene.brightness || stages.add(Cesium.PostProcessStageLibrary.createBrightnessStage());
  viewer.scene.brightness.enabled = true;
  viewer.scene.brightness.uniforms.brightness = 1.2;
  viewer.scene.globe.depthTestAgainstTerrain = true;
  viewer._cesiumWidget._creditContainer.style.display = 'none';

  viewer.shadows = false; //开启阴影效果
  viewer.shadowMap.darkness = 0.8; //阴影透明度--越大越透明
  //去cesium logo水印 或 css
  viewer.cesiumWidget.creditContainer.style.display = 'none'; //去cesium logo水印

  // 场景设置
  viewer.scene.highDynamicRange = true;
  viewer.scene.postProcessStages.bloom.enabled = true;
  viewer.scene.postProcessStages.bloom.uniforms.contrast = 255;
  viewer.scene.postProcessStages.bloom.uniforms.brightness = 0.02;
  viewer.scene.postProcessStages.bloom.uniforms.glowOnly = false;
  viewer.scene.postProcessStages.bloom.uniforms.delta = 3.1;
  viewer.scene.postProcessStages.bloom.uniforms.sigma = 5;
  viewer.scene.postProcessStages.bloom.uniforms.stepSize = 0.6;
  viewer.scene.postProcessStages.bloom.uniforms.isSelected = false;
  viewer.scene.postProcessStages.bloom.uniforms.selectedBloom = 10;
  viewer.scene.postProcessStages.bloom.uniforms.bloomColor = Cesium.Color.fromCssColorString('#fafafa');
  viewer.scene.moon.show = false;
  viewer.scene.undergroundMode = false;
  viewer.scene.terrainProvider.isCreateSkirt = false;
  viewer.scene.globe.showGroundAtmosphere = false;
  viewer.scene.globe.enableLighting = false;
  viewer.scene.fog.enabled = false;
};

// 多个3dtiles模型加载
// urls 多个3dtiles模型地址  loadCb 加载完成的回调
// urls<{url,options}>
export const loadTilesets = async (viewer, urls, loadCb) => {
  const tilesets = urls.map(item => {
    const { url, options } = item;
    let params = { url };

    if (typeof options === 'object') {
      Object.assign(params, options);
    }
    const tile = viewer.scene.primitives.add(new Cesium.Cesium3DTileset(params));
    return tile.readyPromise;
  });
  let result = await Promise.all(tilesets);
  loadCb && loadCb(result);
};

/* export const addSky = () => {
  // 天空盒就是three中的cubeTexture
  const sky = new SkyBoxOnGround({
    sources: {
      positiveX: `/src/assets/skyBox/px.jpg`,
      negativeX: `/src/assets/skyBox/nx.jpg`,
      positiveY: `/src/assets/skyBox/py.jpg`,
      negativeY: `/src/assets/skyBox/ny.jpg`,
      positiveZ: `/src/assets/skyBox/pz.jpg`,
      negativeZ: `/src/assets/skyBox/nz.jpg`,
    },
  })
  viewer.scene.skyBox = sky
} */

//实现楼房切换效果
export const handleDefaultModelEffect_1 = (viewer, tile) => {
  // 处理模型着色
  tile.style = new Cesium.Cesium3DTileStyle({
    color: {
      conditions: [
        ['Number(${Elevation})<20', "color('rgb(25, 211, 226)',0.0)"],

        ['Number(${Elevation})>20', "color('rgb(25, 211, 226)',1)"],
        //['true', "color('rgb(42, 122, 237)',1)"]
      ],
      show: false,
    },
  });
  // 创建纹理对象
  const dayTexture = new Cesium.Texture({
    context: viewer.scene.context, // 获取 Cesium 的 WebGL 上下文
    source: '/src/assets/sky.jpg', // 白天纹理的路径
  });

  const nightTexture = new Cesium.Texture({
    context: viewer.scene.context, // 获取 Cesium 的 WebGL 上下文
    source: '/src/assets/night.jpg', // 夜晚纹理的路径
  });
  const customShader = new Cesium.CustomShader({
    uniforms: {
      u_BaseHeight: {
        type: Cesium.UniformType.FLOAT,
        value: 380.0,
      },
      u_MaxHeight: {
        type: Cesium.UniformType.FLOAT,
        value: 320.0,
      },
      u_isDark: {
        type: Cesium.UniformType.BOOLEAN,
        value: false, // 这里需要确保值正确
      },
      u_TextureDay: {
        type: Cesium.UniformType.SAMPLER_2D,
        value: dayTexture, // 使用创建的纹理对象
      },
      u_TextureNight: {
        type: Cesium.UniformType.SAMPLER_2D,
        value: nightTexture, // 使用创建的纹理对象
      },
    },
    mode: Cesium.CustomShaderMode.MODIFY_MATERIAL,
    lightingModel: Cesium.LightingModel.PBR,
    vertexShaderText: /*glsl*/ `
      void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput) {
        v_normalMC = vsInput.attributes.normalMC;
      }
    `,
    fragmentShaderText: /*glsl*/ `
  // 计算IBL反射
  vec3 getIBLReflection(vec3 positionEC, vec3 normalEC) {
    // 计算反射方向
    vec3 eyeToSurfaceDir = normalize(positionEC);
    vec3 direction = reflect(eyeToSurfaceDir, normalEC);

    // 计算反射坐标
    vec3 coord = normalize(vec3(czm_inverseViewRotation * direction));

    // 纹理采样坐标计算
    float texCoordY = (coord.z - coord.y) / 3.0;
    vec2 textureCoord = vec2(coord.x, texCoordY);

    // 根据白天和夜晚的状态选择合适的纹理
    vec3 resCol = u_isDark
      ? texture(u_TextureNight, textureCoord).rgb
      : texture(u_TextureDay, textureCoord).rgb;

    return resCol;
  }

  // 夜景下的光带特效
  vec3 getColorStripe(vec3 positionMC, float czm_h) {
    vec3 col = vec3(0.1, 0.2, 0.2);

    // 条纹间隔和宽度
    float heightInterval = 50.0;
    float lineWidth = 5.0;

    // 对高度进行模运算，生成条纹
    float modRes = mod(positionMC.y, heightInterval);
    float iTime = czm_frameNumber / 120.0;

    // 计算纹理坐标，使用时间偏移量来创建动态效果
    float textureX = fract(positionMC.x / 50.0);
    float textureY = fract(positionMC.y / 50.0) - iTime;

    // 获取高亮条纹颜色
    vec3 highLightCol = texture(u_colorTexture, vec2(textureX, textureY)).rgb;

    // 如果modRes小于lineWidth，就将颜色设置为高亮颜色
    if (modRes < lineWidth) {
      col = highLightCol;
    } else {
      // 否则保持原来的颜色，并根据高度调整亮度
      col *= czm_h;
    }

    return col;
  }

  void main() {
    // 获取反射光和条纹效果
    vec3 resCol = getIBLReflection(v_positionEC, v_normalMC);
    vec3 col = getColorStripe(v_positionMC, czm_h);

    // 最终颜色合成
    material.diffuse = resCol + col;
  }
    `,
  });

  tile.customShader = customShader;
};

//实现楼房切换效果
export const handleDefaultModelEffect = tile => {
  // 处理模型着色
  tile.style = new Cesium.Cesium3DTileStyle({
    color: {
      conditions: [
        ['Number(${Elevation})<20', "color('rgb(25, 211, 226)',0.0)"],

        ['Number(${Elevation})>20', "color('rgb(25, 211, 226)',1)"],
        //['true', "color('rgb(42, 122, 237)',1)"]
      ],
      show: false,
    },
  });
  const customShader = new Cesium.CustomShader({
    uniforms: {
      u_BaseHeight: {
        type: Cesium.UniformType.FLOAT,
        value: 660.0,
      },
      u_MaxHeight: {
        type: Cesium.UniformType.FLOAT,
        value: 520.0,
      },
      u_isDark: {
        type: Cesium.UniformType.BOOL,
        value: false,
      },
      u_TextureNight: {
        type: Cesium.UniformType.SAMPLER_2D,
        value: new Cesium.TextureUniform({
          url: '/src/assets/night.jpg',
        }),
      },
      u_TextureDay: {
        type: Cesium.UniformType.SAMPLER_2D,
        value: new Cesium.TextureUniform({
          url: '/src/assets/sky.jpg',
        }),
      },
      u_colorTexture: {
        type: Cesium.UniformType.SAMPLER_2D,
        value: new Cesium.TextureUniform({
          url: '/src/assets/color.png',
        }),
      },
    },
    mode: Cesium.CustomShaderMode.MODIFY_MATERIAL,
    lightingModel: Cesium.LightingModel.PBR,
    vertexShaderText: /*glsl*/ `
            void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput) {

            }
       `,
    fragmentShaderText: /*glsl*/ `
        //白天：使用IBL贴图，贴图设置为天空图片
        vec3 getIBLReflection(vec3 positionEC,vec3 normalEC){
          // 在Cesium中，只需要positionEC
          vec3 eyeToSurfaceDir=normalize(positionEC);
          // 生成一个反射向量
          vec3 direction=reflect(eyeToSurfaceDir,normalEC);
          vec3 coord = normalize(vec3(czm_inverseViewRotation * direction));
          vec3 resCol=vec3(0.);
          // 如果是夜晚，使用夜间的反射贴图
          if(u_isDark){
            resCol=texture2D(u_TextureNight,vec2(coord.x, (coord.z - coord.y) / 3.0)).rgb;
          }else{
            resCol=texture2D(u_TextureDay,vec2(coord.x, (coord.z - coord.y) / 3.0)).rgb;
          }
          return resCol;
        }
        // 夜景下，再添加光带特效
        vec3 getColorStripe(vec3 positionMC,float czm_h){
          vec3 col=vec3(0.1,0.2,0.2);
          float heightInterval=50.;
          float lineWidth=5.;
          // 对z轴坐标和高度取模，这样能实现条纹
          float modRes=mod(positionMC.y,heightInterval);
          float iTime=czm_frameNumber/120.;
          // 构造高亮块的颜色
          // 构造uv坐标，其中v坐标需要给一个时间，不断取颜色
          float textureX=fract(positionMC.x/50.);
          float textureY=fract(positionMC.y/50.)-iTime;
          vec3 highLightCol=texture2D(u_colorTexture,vec2(textureX,textureY)).rgb;
          // modRes小于lineWidth的时候，就说明要高亮
          if(modRes<lineWidth){
            col=highLightCol;
          }else{
            col*=czm_h;
          }
          return col;
        }
        void fragmentMain(FragmentInput fsInput,inout czm_modelMaterial material) {
          vec3 positionMC=fsInput.attributes.positionMC;
          vec3 positionWC=fsInput.attributes.positionWC;
          vec3 positionEC=fsInput.attributes.positionEC;
          vec3 normalEC=fsInput.attributes.normalEC;
          // 得到一个czm_h坐标，这个坐标是0-1范围，代表了地上高度
          float czm_h=clamp(0.0,1.0,(positionMC.z-u_BaseHeight)/u_MaxHeight);
          //得到一个cam_x坐标，这个坐标是0-1范围，代表建筑宽度
          float czm_x = clamp(0.0,1.0,(positionMC.x-u_BaseHeight)/u_MaxHeight);
          // 得到一个cam_y坐标，这个坐标是0-1范围，代表建筑高度
          float czm_y = clamp(0.0,1.0,(positionMC.y-u_BaseHeight)/u_MaxHeight);
          vec3 resCol=vec3(0.0,0.5,0.5);
  
          vec3 gradientColor1=vec3(0.1,0.1,0.1);
          vec3 gradientColor2=vec3(0.1,0.2,0.6);
          // 给模型一个渐变色
          resCol=mix(gradientColor1,gradientColor2,czm_h);

          // 让时间来回运动
          float iTime=czm_frameNumber/180.0;
          iTime=abs(fract(iTime)*2.-1.);
          // 假设我们只需要一条固定光带 宽度为0.05
          // 使用step函数优化if-else
          float czm_diff = step(0.005, abs(czm_y - iTime));
          if (u_isDark) {
            // 夜间效果：条纹光带 + 扫描光圈
            vec3 colorStripe = getColorStripe(positionMC, czm_h);
            resCol += (resCol * (1.0 - czm_diff) * 10.0) + colorStripe * 3.0;
          } else {
              // 白天效果：使用IBL贴图（天空图片）
              vec3 IBLCol = getIBLReflection(positionEC, normalEC);
              resCol = IBLCol;
          }
      
          // 设置材质的diffuse颜色
          material.diffuse = resCol;
          material.alpha = 1.0; // 设为完全不透明
        }
         `,
  });
  tile.customShader = customShader;
};

// 视角飞到全局
export const flyToDefaultView = viewer => {
  viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(113.95, 30.19, 34000),
    duration: 2,
    orientation: {
      heading: Cesium.Math.toRadians(35.0),
      pitch: Cesium.Math.toRadians(-37.0),
      roll: 0.0,
    },
  });
};

// 添加水域
export const addRiver = async viewer => {
  const url = '/src/assets/water.json';
  const data = await new Cesium.GeoJsonDataSource.load(url);
  const entities = data.entities.values;
  // 水域材质只有primitive的appearance才有，所以不能直接add
  //viewer.dataSources.add(dataSource)
  // 要先把坐标解析出来，构造primitive来添加
  const instances = [];
  entities.forEach(ent => {
    // 获取每个实体下的多边形坐标
    const hierarchy = ent.polygon.hierarchy.getValue();
    let positions = hierarchy.positions;
    // 坐标系不同，需要转换,将gcj02转wgs84
    positions = positions.map(position => {
      // 笛卡尔转经纬度
      const [lng, lat] = cartesian3ToDegreesHeight(position);
      // gcj02转wgs84
      const [newLng, newLat] = CoordTransform.GCJ02ToWGS84(lng, lat);
      // 经纬度转笛卡尔
      return new Cesium.Cartesian3.fromDegrees(newLng, newLat, 0);
    });
    const geometryInstance = new Cesium.GeometryInstance({
      geometry: new Cesium.PolygonGeometry({
        polygonHierarchy: new Cesium.PolygonHierarchy(positions),
        height: 0,
        extrudedHeight: 0,
        vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
      }),
    });
    instances.push(geometryInstance);
  });
  // 添加水面材质
  const appearance = new Cesium.EllipsoidSurfaceAppearance({
    material: new Cesium.Material({
      fabric: {
        type: 'Water',
        uniforms: {
          baseWaterColor: new Cesium.Color(0, 0.2, 1, 1),
          // specularMap可以指定水面的渲染区域
          //specularMap: "/src/assets/earthspec1k.jpg",
          specularMap: Cesium.Material.DefaultImageId,
          normalMap: '/src/assets/waterNormals.jpg',
          frequency: 10000.0,
          animationSpeed: 0.6,
          amplitude: 1.0,
          specularIntensity: 10,
          fadeFactor: 1.0,
          u_isDark: false,
        },
        source: /*glsl*/ `
          #define TAU 6.28318530718
          #define MAX_ITER 5

          uniform sampler2D specularMap;
          uniform sampler2D normalMap;
          uniform vec4 baseWaterColor;
          uniform vec4 blendColor;
          uniform float frequency;
          uniform float animationSpeed;
          uniform float amplitude;
          uniform float specularIntensity;
          uniform float fadeFactor;

          struct Water{
            vec3 waterCol;
            float alpha;
            vec3 normal;
            float specular;
            float shininess;
          };
          // cesium源码中的water.glsl
          Water getWaterNormalCol(czm_materialInput materialInput){
            Water water;
            float time = czm_frameNumber * animationSpeed;
            // fade is a function of the distance from the fragment and the frequency of the waves
            float fade = max(1.0, (length(materialInput.positionToEyeEC) / 10000000000.0) * frequency * fadeFactor);
            float specularMapValue = texture2D(specularMap, materialInput.st).r;
            // note: not using directional motion at this time, just set the angle to 0.0;
            vec4 noise = czm_getWaterNoise(normalMap, materialInput.st * frequency, time, 0.0);
            vec3 normalTangentSpace = noise.xyz * vec3(1.0, 1.0, (1.0 / amplitude));
            // fade out the normal perturbation as we move further from the water surface
            normalTangentSpace.xy /= fade;
            // attempt to fade out the normal perturbation as we approach non water areas (low specular map value)
            normalTangentSpace = mix(vec3(0.0, 0.0, 50.0), normalTangentSpace, specularMapValue);

            normalTangentSpace = normalize(normalTangentSpace);

            // get ratios for alignment of the new normal vector with a vector perpendicular to the tangent plane
            float tsPerturbationRatio = clamp(dot(normalTangentSpace, vec3(0.0, 0.0, 1.0)), 0.0, 1.0);
            float alpha=mix(blendColor.a, baseWaterColor.a, specularMapValue) * specularMapValue;
            vec3 col=mix(blendColor.rgb, baseWaterColor.rgb, specularMapValue);
            col+= (0.1 * tsPerturbationRatio);
            water.waterCol=col;
            water.alpha=alpha;
            water.normal= normalize(materialInput.tangentToEyeMatrix * normalTangentSpace);
            water.specular=specularIntensity;
            water.shininess=10.0;
            return water;
          }
          // 搬运自shaderToy
          vec3 getNightWaterCol(czm_materialInput materialInput){
            vec2 uv=materialInput.st;
            uv*=5.0;
            #ifdef SHOW_TILING
	          vec2 p = mod(uv*TAU*2.0, TAU)-250.0;
            #else
            vec2 p = mod(uv*TAU, TAU)-250.0;
            #endif
	          vec2 i = vec2(p);
	          float c = 1.0;
	          float inten = .005;
            float iTime = czm_frameNumber * animationSpeed;
            float time = iTime * .5+23.0;
	          for (int n = 0; n < MAX_ITER; n++)
	          {
	          	float t = time * (1.0 - (3.5 / float(n+1)));
	          	i = p + vec2(cos(t - i.x) + sin(t + i.y), sin(t - i.y) + cos(t + i.x));
	          	c += 1.0/length(vec2(p.x / (sin(i.x+t)/inten),p.y / (cos(i.y+t)/inten)));
	          }
	          c /= float(MAX_ITER);
	          c = 1.17-pow(c, 1.4);
	          vec3 colour = vec3(pow(abs(c), 8.0));
              colour = clamp(colour + vec3(0.0, 0.35, 0.5), 0.0, 1.0);

	          #ifdef SHOW_TILING
	          // Flash tile borders...
	          vec2 pixel = 2.0 / gl_FragCoord.xy;
	          uv *= 2.0;
	          float f = floor(mod(iTime*.5, 2.0)); 	// Flash value.
	          vec2 first = step(pixel, uv) * f;		   	// Rule out first screen pixels and flash.
	          uv  = step(fract(uv), pixel);				// Add one line of pixels per tile.
	          colour = mix(colour, vec3(1.0, 1.0, 0.0), (uv.x + uv.y) * first.x * first.y); // Yellow line
	          #endif
            return colour;
          }

          czm_material czm_getMaterial(czm_materialInput materialInput)
          {
              czm_material material = czm_getDefaultMaterial(materialInput);
              // 获取cesium官方提供的水面材质
              Water water=getWaterNormalCol(materialInput);
              vec3 waterNight=getNightWaterCol(materialInput);
              if(u_isDark){
                material.diffuse = waterNight;
                material.alpha=1.0;
              }else{
                // fade out water effect as specular map value decreases
                material.alpha = water.alpha;
                material.diffuse=water.waterCol;
                material.normal =water.normal;
                material.specular = water.specular;
                material.shininess = water.shininess;
              }
              return material;
          }

                  `,
      },
    }),
  });
  return viewer.scene.primitives.add(
    new Cesium.GroundPrimitive({
      geometryInstances: instances,
      appearance,
    })
  );
};

/**
 * 实现早晚变化效果
 * @param {Cesium.Viewer} viewer - viewer对象。
 * @param {Cesium.Cesium3DTileset} tileset 当前的3d瓦片
 * @param {Cesium.Primitive} waterPrimitive 水面的primitive
 */
export const handleUpdateScene = (viewer, tileset, waterPrimitive) => {
  let _angle = 0;
  // 更新场景
  const updateScene = () => {
    // 从世界坐标原点到太阳的单位向量
    const sunDirection = Cesium.Cartesian3.normalize(viewer.scene.sun._boundingVolume.center, new Cesium.Cartesian3());
    // 世界坐标到相机的单位向量
    const cameraDirection = Cesium.Cartesian3.normalize(viewer.camera.position, new Cesium.Cartesian3());
    //手动调试时间看白天和夜晚水域效果
    // const clock = viewer.clock;
    // const customTime = Cesium.JulianDate.fromIso8601('2024-01-01T20:00:00Z');
    // clock.currentTime = customTime; // 设置当前时间
    // 点乘相机到太阳光两个向量，确定夹角,如果夹角余弦小于0，说明到了晚上，渲染夜间效果
    const angle = parseFloat(Cesium.Cartesian3.dot(cameraDirection, sunDirection).toFixed(3));
    //角度没变化，说明时间也没变，不更新
    if (angle === _angle) return false;

    if (angle >= 0) {
      //白天的逻辑，取消bloom等后处理效果
      viewer.postProcessStages.bloom.enabled = false; // 关闭 bloom 效果
      viewer.scene.postProcessStages.ambientOcclusion.enabled = true; // 开启环境光遮蔽
      if (waterPrimitive) {
        waterPrimitive.appearance.material.uniforms.u_isDark = false; // 白天时设置水面为正常
      }
    }
    if (angle < 0) {
      // 夜晚的逻辑，开启bloom,并设置customShader和water
      viewer.postProcessStages.bloom.enabled = true; // 开启 bloom 效果
      viewer.scene.postProcessStages.ambientOcclusion.enabled = false; // 关闭环境光遮蔽
      if (waterPrimitive) {
        waterPrimitive.appearance.material.uniforms.u_isDark = true; // 夜晚时设置水面为黑暗
      }
    }
    _angle = angle;
  };
  viewer.scene.postRender.addEventListener(() => updateScene());
};
